
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport"
	content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="xuejianxinokok">
<title>vue-cropper demo</title>
<link rel="stylesheet"
	href="https://cdn.jsdelivr.net/npm/vue-cropper@1.0.2/dist/index.css" />
<link rel="stylesheet"
	href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" />
<style type="text/css">

/* 
.cut {
	width:800px; 
	height:800px; 
} 

*/
.cut {
	width: 400px;
	height: 400px;
}

.c-item {
	max-width: 800px;
	margin: 10px auto;
	margin-top: 10px;
}

.c-item {
	display: block;
	user-select: none;
}

.model {
	position: fixed;
	z-index: 10;
	width: 100vw;
	height: 100vh;
	overflow: auto;
	top: 0;
	left: 0;
	background: rgba(0, 0, 0, 0.8);
}

.model-show {
	display: flex;
	justify-content: center;
	align-items: center;
	width: 100vw;
	height: 100vh;
}

.model img {
	display: block;
	margin: auto;
	max-width: 80%;
	user-select: none;
	background-position: 0px 0px, 10px 10px;
	background-size: 20px 20px;
	background-image: linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee
		75%, #eee 100%),
		linear-gradient(45deg, #eee 25%, white 25%, white 75%, #eee 75%, #eee
		100%);
}
</style>
</head>
<body>



	<div id="app" class="container-fluid">



		<div class="row">
			<div class="col-4 border  border-info">
				<div class="model" v-show="model" @click="model = false">
					<div class="model-show">
						<img :src="modelSrc" alt="">
					</div>
				</div>
				<p><a href="https://github.com/xyxiao001/vue-cropper" target="_blank">官网</a>  </p>
				<div class="cut">
					<vue-cropper ref="cropper" :img="option.img"
						:output-size="option.size" :output-type="option.outputType"
						:info="true" :full="option.full" :fixed="fixed"
						:fixed-number="fixedNumber" :can-move="option.canMove"
						:can-move-box="option.canMoveBox" :fixed-box="option.fixedBox"
						:original="option.original" :auto-crop="option.autoCrop"
						:auto-crop-width="option.autoCropWidth"
						:auto-crop-height="option.autoCropHeight"
						:center-box="option.centerBox" :high="option.high"
						:max-img-size="option.max" mode="cover" @real-time="realTime"
						@img-load="imgLoad" @crop-moving="cropMoving"
						@img-moving="imgMoving"></vue-cropper>
				</div>
				<div class="show-preview"
					:style="{'width': previews.w + 'px', 'height': previews.h + 'px',  'overflow': 'hidden', 'margin': '5px'}">
					<div :style="previews.div">
						<img :src="previews.url" :style="previews.img">
					</div>
				</div>
				<div>
					<button @click="changeImg" class="btn  btn-outline-primary btn-sm">changeImg</button>
					<label class="btn" for="uploads">upload</label> <input type="file"
						id="uploads" style="position: absolute; clip: rect(0, 0, 0, 0);"
						accept="image/png, image/jpeg, image/gif, image/jpg"
						@change="uploadImg($event, 1)">
					<button @click="startCrop" v-if="!crap"
						class="btn btn-outline-primary btn-sm">start</button>
					<button @click="stopCrop" v-else
						class="btn btn-outline-primary btn-sm">stop</button>
					<button @click="clearCrop" class="btn btn-outline-primary btn-sm">clear</button>




					<button @click="finish('base64')"
						class="btn btn-outline-primary btn-sm">preview(base64)</button>
					<button @click="finish('blob')"
						class="btn btn-outline-primary btn-sm">preview(blob)</button>
					<a @click="down('base64')" class="btn btn-outline-primary btn-sm">download(base64)</a>
					<a @click="down('blob')" class="btn btn-outline-primary btn-sm">download(blob)</a>
					<a @click="() => option.img = ''"
						class="btn btn-outline-primary btn-sm">清除图片</a>
					<div style="display: block; width: 100%;">
						<label class="c-item"> <span>上传图片是否显示原始宽高 (针对大图
								可以铺满)</span> <input type="checkbox" v-model="option.original">
							<span>original: {{ option.original}}</span>
						</label> <label class="c-item"> <span>能否拖动图片</span> <input
							type="checkbox" v-model="option.canMove">
						</label> <label class="c-item"> <span>能否拖动截图框</span> <input
							type="checkbox" v-model="option.canMoveBox"> <span>canMoveBox:
								{{ option.canMoveBox}}</span>
						</label> <label class="c-item"> <span>截图框固定大小</span> <input
							type="checkbox" v-model="option.fixedBox"> <span>fixedBox:
								{{ option.fixedBox}}</span>
						</label> <label class="c-item"> <span>是否输出原图比例的截图</span> <input
							type="checkbox" v-model="option.full"> <span>full:
								{{ option.full}}</span>
						</label> <label class="c-item"> <span>是否自动生成截图框</span> <input
							type="checkbox" v-model="option.autoCrop"> <span>autoCrop:
								{{ option.autoCrop}}</span>
						</label> <label class="c-item"> <span>是否根据dpr生成适合屏幕的高清图片</span> <input
							type="checkbox" v-model="option.high"> <span>high:
								{{ option.high}}</span>
						</label> <label class="c-item"> <span>截图框是否限制在图片里(只有在自动生成截图框时才能生效)</span>
							<input type="checkbox" v-model="option.centerBox"> <span>centerBox:
								{{ option.centerBox}}</span>
						</label> <label class="c-item">
							<p>输出图片格式</p> <label>jpg <input type="radio" name="type"
								value="jpeg" v-model="option.outputType">
						</label> <label>png <input type="radio" name="type" value="png"
								v-model="option.outputType">
						</label> <label>webp <input type="radio" name="type" value="webp"
								v-model="option.outputType">
						</label>
						</label>
					</div>
				</div>



			</div>
			<div class="col border  border-info">
				<p>

					<button @click="refreshCrop" class="btn btn-outline-primary btn-sm">refresh</button>
					<button @click="changeScale(1)"
						class="btn btn-outline-primary btn-sm">+</button>
					<button @click="changeScale(-1)"
						class="btn btn-outline-primary btn-sm">-</button>
					<button @click="rotateLeft" class="btn btn-outline-primary btn-sm">rotateLeft</button>
					<button @click="rotateRight" class="btn btn-outline-primary btn-sm">rotateRight</button>
					<button class="btn btn-info btn-sm" @click="frontCrop">前端端裁剪</button>
					
				</p>


				<img alt="图片" src="" style="width: 95%;border-style:solid; border-width:1px;border-color: blue;"  ref="img1">
				<br/>
				<br/>
				<br/>
				<p>
				<button class="btn btn-success btn-sm" @click="backendCrop">后端裁剪↓</button>
				</p>
				<img alt="图片" src="" style="width: 95%;border-style:solid; border-width:1px;border-color: red"  ref="img2">
			</div>

		</div>







	</div>
	<script src="https://cdn.jsdelivr.net/npm/vue@3.2.1/dist/vue.global.js"></script>
	<script
		src="https://cdn.jsdelivr.net/npm/vue-cropper@1.0.2/dist/vue-cropper.umd.js"></script>
	<script src="./resources/js/http.js"></script>
	<script type="text/javascript">
const app = Vue.createApp({
  data () {
    return {
    model: false,
    modelSrc: '',
    crap: false,
    previews: {},
    imgIndex:0,//当前img
    lists: [
      {img: '3.jpg'},
      {img: '2.jpg'},
      {img: '1.jpg'},
    ],
    option: {
      img: '3.jpg',
      size: 1,
      full: true, //是否输出原图比例的截图
      outputType: 'png',
      canMove: true,
      fixedBox: false,
      original: false, //上传图片按照原始比例渲染上传图片按照原始比例渲染
      canMoveBox: true,
      autoCrop: true,
      // 只有自动截图开启 宽度高度才生效
      autoCropWidth: 100,
      autoCropHeight: 100,
      centerBox: true,
      high: true,
      max: 99999,
    },
    show: true,
    fixed: false, //是否开启截图框宽高固定比例
    fixedNumber: [1, 1], //截图框的宽高比例
    }
  },
  components: {},
  methods: {
    changeImg() {
      this.imgIndex=(++this.imgIndex)%this.lists.length
      //this.option.img = this.lists[~~(Math.random() * this.lists.length)].img;
      this.option.img = this.lists[this.imgIndex].img;
    },
    startCrop() {
      // start
      this.crap = true;
      this.$refs.cropper.startCrop();
    },
    stopCrop() {
      //  stop
      this.crap = false;
      this.$refs.cropper.stopCrop();
    },
    clearCrop() {
      // clear
      this.$refs.cropper.clearCrop();
    },
    refreshCrop() {
      // clear
      this.$refs.cropper.refresh();
    },
    changeScale(num) {
      num = num || 1;
      this.$refs.cropper.changeScale(num);
    },
    rotateLeft() {
      this.$refs.cropper.rotateLeft();
    },
    rotateRight() {
      this.$refs.cropper.rotateRight();
    },
    finish(type) {
      // 输出
      // var test = window.open('about:blank')
      // test.document.body.innerHTML = '图片生成中..'
      if (type === 'blob') {
        this.$refs.cropper.getCropBlob((data) => {
          console.log(data);
          var img = window.URL.createObjectURL(data);
          this.model = true;
          this.modelSrc = img;
        });
      } else {
        this.$refs.cropper.getCropData((data) => {
          this.model = true;
          this.modelSrc = data;
        });
      }
    },
    // 实时预览函数
    realTime(data) {
      this.previews = data;
      console.log(data);
    },

    finish2(type) {
      this.$refs.cropper2.getCropData((data) => {
        this.model = true;
        this.modelSrc = data;
      });
    },
    finish3(type) {
      this.$refs.cropper3.getCropData((data) => {
        this.model = true;
        this.modelSrc = data;
      });
    },
    down(type) {
      // event.preventDefault()
      var aLink = document.createElement('a');
      aLink.download = 'demo';
      // 输出
      if (type === 'blob') {
        this.$refs.cropper.getCropBlob((data) => {
          this.downImg = window.URL.createObjectURL(data);
          aLink.href = window.URL.createObjectURL(data);
          aLink.click();
        });
      } else {
        this.$refs.cropper.getCropData((data) => {
          this.downImg = data;
          aLink.href = data;
          aLink.click();
        });
      }
    },

    uploadImg(e, num) {
      //上传图片
      // this.option.img
      var file = e.target.files[0];
      if (!/\.(gif|jpg|jpeg|png|bmp|GIF|JPG|PNG)$/.test(e.target.value)) {
        alert('图片类型必须是.gif,jpeg,jpg,png,bmp中的一种');
        return false;
      }
      var reader = new FileReader();
      reader.onload = (e) => {
        let data;
        if (typeof e.target.result === 'object') {
          // 把Array Buffer转化为blob 如果是base64不需要
          data = window.URL.createObjectURL(new Blob([e.target.result]));
        } else {
          data = e.target.result;
        }
        if (num === 1) {
          this.option.img = data;
        } else if (num === 2) {
          this.example2.img = data;
        }
      };
      // 转化为base64
      // reader.readAsDataURL(file)
      // 转化为blob
      reader.readAsArrayBuffer(file);
    },
    imgLoad(msg) {
      console.log(msg);
    },
    cropMoving(data) {
      console.log(data, '截图框当前坐标');
      this.cropAxis=data.axis
    },
	imgMoving(data) {
      console.log(data, '图片坐标');
    },
    
    frontCrop(){//前端裁剪
    	this.$refs.cropper.getCropData(data => {
    		  // do something
    		  console.log(data) 
    		  this.$refs.img1.src=data;
    		})
    },
    
    backendCrop(){//后端裁剪
    	
    	//发现的获取裁剪后的实际图像大小
    	
    	let cropper=this.$refs.cropper;
    	
       
    
    	//let box=this.cropAxis;
    	let img= cropper.getImgAxis();	//获取图片基于容器的坐标点
    	let rotate=cropper.rotate*90;
    	//let scale=this.$refs.cropper.scale;
    	let crop= cropper.getCropAxis() //获取截图框基于容器的坐标点
    	//console.log(cropAxis)
    	//let w= this.$refs.cropper.cropW	;//截图框宽度
    	//let h= this.$refs.cropper.cropH	;//截图框高度
    	
    	let trueWidth=cropper.trueWidth
    	let trueHeight = cropper.trueHeight
    	
    	console.log(trueWidth,trueHeight)
    	
    	
    	let imgW=img.x2-img.x1       //被裁剪图像宽
		let imgH=img.y2-img.y1       //被裁剪图像高
		
		
		let x=crop.x1-img.x1         //裁剪框左上角x,相对于原始图像
		let y=crop.y1-img.y1         //裁剪框左上角y,相对于原始图像
		
		
		
		let w=crop.x2-crop.x1        //裁剪框宽
		let h=crop.y2-crop.y1        //裁剪框高
		
		//按cavas 比例缩放
		const CANVAS_W=940
		const CANVAS_H=700
		
		let cropRatio=w/h
		let canvasRatio=CANVAS_W/CANVAS_H  //1.34
		//let imgRatio=imgW/imgH //图片比例
		let scale=0 //缩放比例
		
		if(cropRatio>=canvasRatio){
			//scale=CANVAS_W/imgW
			//scale=w/imgW   //裁剪框宽和图像宽 比值
			scale=CANVAS_W/w   //裁剪框宽和图像宽 比值
			//scale=imgW/w   //裁剪框宽和图像宽 比值
		}else{
			//scale=CANVAS_H/imgH
			//scale=h/imgH   //裁剪框高和图像高 比值
			scale=CANVAS_H/h   //裁剪框高和图像高 比值
			//scale=imgW/h   //裁剪框高和图像高 比值
		}
    	
         
		
		imgW=imgW*scale
		imgH=imgH*scale
		x=x*scale
		y=y*scale
		w=w*scale
		h=h*scale
		
		
		
		
		
    	
    	let option={
    		imgId:this.option.img,  //图像id
    		imgRotate:rotate,       //旋转
    		//imgScale :scale,      //缩放
    		//imgX:img.x1,          //被裁剪图像左上角x
    		//imgY:img.y1,          //被裁剪图像左上角y
    		imgW,      //被裁剪图像宽
    		imgH,      //被裁剪图像高
    		// 下边的计算非常重要
			// 参考 https://www.cnblogs.com/tugenhua0707/p/8859291.html 计算裁剪区域
			// 左阴影宽，左阴影宽 = 裁剪区域的左偏移值 - 图片本身的左偏移值；
			// 上阴影高 = 裁剪区域的上偏移值 - 图片上偏移值
    		x,       //裁剪框左上角x,相对于原始图像
    		y,       //裁剪框左上角y,相对于原始图像
    		w,       //裁剪框宽
    		h        //裁剪框高
    	}
    	
    	console.log(option)
    	$http.post('/cropper',option).then(res=>{
    		if(res.success){
    		   this.$refs.img2.src=res.imgBase64;
    		}else{
    			alert('操作失败！');
    		}
    	})
    }
  },
  mounted: function() {
    // console.log(window['vue-cropper'])
  },
});
app.component('vue-cropper', window['vue-cropper'].VueCropper);
app.mount('#app')

//window.getComputedStyle(element)
//$0.style.transform

</script>



</body>
</html>
